//
//  PhysicsDrawer.cpp
//  Olivia
//
//  Created by 钟昆达 on 13-4-12.
//
//

#include "PhysicsDrawer.h"
#include "OgreManualObject.h"
#include "OgreSceneManager.h"

namespace Olivia {
    
    static int32 CircleSegmentCount = 16;
    static int32 ReserveVertexCount = 4096;
    static int32 ReserveIndexCount = 4096 * 2;
    
    PhysicsDraw::PhysicsDraw(Ogre::ManualObject *_prim) : m_primitives(_prim),
        m_v_count(0), m_i_count(0)
    {
        if (0 == m_primitives)
        {
            return;
        }
        m_primitives->setDynamic(true);
        // for solid objects
        m_primitives->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_LINE_LIST, Ogre::ResourceGroupManager::INTERNAL_RESOURCE_GROUP_NAME);
        m_primitives->estimateIndexCount(ReserveIndexCount);
        m_primitives->estimateVertexCount(ReserveVertexCount);
        m_primitives->position(Ogre::Vector3(.0f, .0f, .0f));
        m_primitives->index(0);
        m_primitives->index(0);
        m_primitives->end();
        m_primitives->beginUpdate(0);
        m_primitives->end();
        // for wire frame objects
        m_primitives->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_TRIANGLE_LIST, Ogre::ResourceGroupManager::INTERNAL_RESOURCE_GROUP_NAME);
        m_primitives->estimateIndexCount(ReserveIndexCount);
        m_primitives->estimateVertexCount(ReserveVertexCount);
        m_primitives->position(Ogre::Vector3(.0f, .0f, .0f));
        m_primitives->triangle(0, 0, 0);
        m_primitives->end();
        m_primitives->beginUpdate(1);
        m_primitives->end();
        
    }
    PhysicsDraw::~PhysicsDraw()
    {
        if (m_primitives) {
            m_primitives->_getManager()->destroyManualObject(m_primitives);
        }
    }
    
    void PhysicsDraw::DrawPolygon(const b2Vec2 *vertices, int32 v_count, const b2Color &color)
    {
        Polygon pol = {(int32)m_polygon_vertices.size(), v_count, color};
        m_wf_polygons.push_back(pol);
        for (int32 i = 0; i < v_count; ++i)
        {
            m_polygon_vertices.push_back(vertices[i]);
        }
        
        m_i_count += 2 * v_count;
        m_v_count += v_count;
    }
    
    void PhysicsDraw::DrawSolidPolygon(const b2Vec2 *vertices, int32 v_count, const b2Color &color)
    {
        Polygon pol = {(int32)m_polygon_vertices.size(), v_count, color};
        m_solid_polygons.push_back(pol);
        for (int32 i = 0; i < v_count; ++i)
        {
            m_polygon_vertices.push_back(vertices[i]);
        }
        
        m_i_count += 3 * v_count;
        m_v_count += v_count + 1;
    }
    
    void PhysicsDraw::DrawCircle(const b2Vec2 &center, float32 radius, const b2Color &color)
    {
        Circle circle = {center, radius, color};
        m_wf_circles.push_back(circle);
        
        m_i_count += 2 * (CircleSegmentCount + 1);
        m_v_count += CircleSegmentCount - 1;
    }
    
    void PhysicsDraw::DrawSolidCircle(const b2Vec2 &center, float32 radius, const b2Vec2 &axis, const b2Color &color)
    {
        Circle circle = {center, radius, color};
        m_solid_circles.push_back(circle);
        
        m_i_count += 3 * (CircleSegmentCount + 1);
        m_v_count += CircleSegmentCount;
    }
    
    void PhysicsDraw::DrawSegment(const b2Vec2 &p1, const b2Vec2 &p2, const b2Color &color)
    {
        Segment seg = {p1, p2, color};
        m_segments.push_back(seg);
        m_i_count += 2;
        m_v_count += 2;
    }
    
    void PhysicsDraw::DrawTransform(const b2Transform &xf)
    {
        m_transforms.push_back(xf);
        m_i_count += 4;
        m_v_count += 4;
    }
    
    void PhysicsDraw::Render()
    {
        if (NULL == m_primitives)
        {
            return;
        }
        
        m_primitives->beginUpdate(0);
        int total_v_count(0);
        for (size_t i = 0; i < m_wf_polygons.size(); ++i)
        {
            const Polygon &poly = m_wf_polygons[i];
            const b2Vec2 *v = &m_polygon_vertices[poly.v_begin + i];
            for (int j = 0; j < poly.v_count; ++j)
            {
                m_primitives->position(Ogre::Vector3(v[j].x, v[j].y, .0f));
                m_primitives->colour(Ogre::ColourValue(poly.color.r, poly.color.g, poly.color.b));
            }
            for (int j = 0; j < poly.v_count - 1; ++j)
            {
                m_primitives->index(poly.v_begin + j);
                m_primitives->index(poly.v_begin + 1 + j);
            }
            m_primitives->index(poly.v_begin + poly.v_count - 1);
            m_primitives->index(poly.v_begin);
            total_v_count += poly.v_count;
        }
        for (size_t i = 0; i < m_wf_circles.size(); ++i)
        {
            const Circle &circle = m_wf_circles[i];
            int begin = total_v_count;
            for (int j = 0; j < CircleSegmentCount; ++j)
            {
                float angle = 3.1415926f * j / CircleSegmentCount;
                m_primitives->position(Ogre::Vector3(sin(angle) * circle.radius + circle.center.x, cos(angle) * circle.radius + circle.center.y, .0f));
                m_primitives->colour(Ogre::ColourValue(circle.color.r, circle.color.r, circle.color.b));
            }
            for (int j = 0; j < CircleSegmentCount - 1; ++j)
            {
                m_primitives->index(begin + j);
                m_primitives->index(begin + j + 1);
            }
            m_primitives->index(begin);
            m_primitives->index(begin + CircleSegmentCount - 1);
            total_v_count += CircleSegmentCount;
        }
        m_primitives->end();
        
        m_primitives->beginUpdate(1);
        for (size_t i = 0; i < m_solid_circles.size(); ++i)
        {
            const Circle &circle = m_solid_circles[i];
            int center = total_v_count++;
            m_primitives->position(circle.center.x, circle.center.y, .0f);
            m_primitives->colour(Ogre::ColourValue(circle.color.r, circle.color.g, circle.color.b));
            
            int begin = total_v_count;
            for (int j = 0; j < CircleSegmentCount; ++j)
            {
                float angle = 2.0f * 3.1415926f * j / CircleSegmentCount;
                m_primitives->position(sin(angle) * circle.radius + circle.center.x, cos(angle) * circle.radius + circle.center.y, .0f);
                m_primitives->colour(Ogre::ColourValue(circle.color.r, circle.color.r, circle.color.b));
            }
            for (int j = 0; j < CircleSegmentCount - 1; ++j)
            {
                m_primitives->index(begin + j);
                m_primitives->index(center);
                m_primitives->index(begin + j + 1);
            }
            m_primitives->index(begin + CircleSegmentCount - 1);
            m_primitives->index(center);
            m_primitives->index(begin);
            total_v_count += CircleSegmentCount;
        }
        m_primitives->end();
        
        m_wf_polygons.resize(0);
        m_polygon_vertices.resize(0);
        m_solid_polygons.resize(0);
        m_wf_circles.resize(0);
        m_solid_circles.resize(0);
        m_i_count = m_v_count = 0;
    }
}